package oceanlife.evostrat;

import java.util.ArrayList;

import oceanlife.OLSet;
import oceanlife.RandomGen;
import oceanlife.log.Logger;
import oceanlife.pathfinding.Pillar;
import oceanlife.pathfinding.Pnt;
import oceanlife.pathfinding.SimpleObstacle;

public class GenePillarHallway implements Gene {

	private double[] genes;
	private double[] stepSizes;
	private double globalLearningRate;
	private double learningRate;
	
	public double getMaxValue(int index){
		if(index%2 == 0){
			return Pillar.maxPosition;
		} else {
			return Pillar.maxRadius;
		}
	}
	public double getMinValue(int index){
		return 0.0;
	}
	@Override
	public void setGenes(double[] genes) {
		this.genes = genes;
	}
	@Override
	public void setStepSizes(double[] stepSizes) {
		this.stepSizes = stepSizes;
	}
	@Override
	public void setGlobalLearningRate(double globalLearningRate) {
		this.globalLearningRate = globalLearningRate;
	}
	@Override
	public void setLearningRate(double learningRate) {
		this.learningRate = learningRate;
	}
	@Override
	public double[] getGenes() {
		return genes;
	}
	@Override
	public double[] getStepSizes() {
		return stepSizes;
	}
	@Override
	public double getGlobalLearningRate() {
		return globalLearningRate;
	}
	@Override
	public double getLearningRate() {
		return learningRate;
	}
	@Override
	public Gene makeChild(RandomGen rand) {
		GenePillarHallway child = new GenePillarHallway();
		child.genes = new double[this.genes.length];
		child.stepSizes = new double[this.genes.length];
		child.globalLearningRate = this.globalLearningRate;
		double globalTau = this.globalLearningRate*rand.getGaussian();
		child.stepSizes[0] = this.stepSizes[0] * Math.exp(this.learningRate*rand.getGaussian()+globalTau);//hack for using single stepsize
		for(int i = 0; i < this.genes.length; i++){
			child.genes[i] = this.genes[i] + child.stepSizes[0] * rand.getGaussian();
			while(child.genes[i] > child.getMaxValue(i) || child.genes[i] < child.getMinValue(i)){
				if(child.genes[i] > child.getMaxValue(i)){
					child.genes[i] = child.getMaxValue(i) - (child.genes[i] - child.getMaxValue(i));
				} else {
					child.genes[i] = child.getMinValue(i) - (child.genes[i] - child.getMinValue(i));
				}
			}
		}
		child.learningRate = this.learningRate;
		return child;
	}
	
	@Override
	public ArrayList<SimpleObstacle> getSimpleObstacles() {
		ArrayList<SimpleObstacle> simpleObs = new ArrayList<SimpleObstacle>();

		Double xOffset = 80.0;
		Double yOffset = 10.0;
		Double pillarSpacing = Pillar.spacing;

		ArrayList<Pillar> pillars = new ArrayList<Pillar>();
		for(int i = 0; i < this.genes.length/2 ; i++){
			pillars.add(new Pillar(this.genes[i*2],this.genes[i*2+1]));
		}

		for(int i = 0; i < pillars.size(); i++){
			simpleObs.add(new SimpleObstacle(pillars.get(i).getPnts(xOffset+(pillarSpacing*i), yOffset)));
		}
		//add corridor walls
		Double wallExtraLength = 1000.0;
		Double wallThickness = 20.0;
		simpleObs.add(new SimpleObstacle(new Pnt[]{new Pnt(xOffset-wallExtraLength,yOffset),
										 new Pnt(xOffset+(pillars.size()*pillarSpacing)+wallExtraLength,yOffset),
										 new Pnt(xOffset+(pillars.size()*pillarSpacing)+wallExtraLength,yOffset-wallThickness),
										 new Pnt(xOffset-wallExtraLength,yOffset-wallThickness)}));
		simpleObs.add(new SimpleObstacle(new Pnt[]{new Pnt(xOffset-wallExtraLength,yOffset+Pillar.maxPosition+wallThickness),
										 new Pnt(xOffset+(pillars.size()*pillarSpacing)+wallExtraLength,yOffset+Pillar.maxPosition+wallThickness),
										 new Pnt(xOffset+(pillars.size()*pillarSpacing)+wallExtraLength,yOffset+Pillar.maxPosition),
										 new Pnt(xOffset-wallExtraLength,yOffset+Pillar.maxPosition)}));

		return simpleObs;
	}

	public static Gene getInitChild() {
		RandomGen rand = RandomGen.getNextRandomGen();
		int pillarAmount = 5;
    	double[] genes = new double[pillarAmount*2];
    	double[] stepSizes = new double[pillarAmount*2];
    	double learningRate = 1.0/Math.sqrt(2*genes.length); // div dimentionality
    	double globalLearningRate = 1.0/Math.sqrt(2*Math.sqrt(genes.length));// div dimentionality
    	for(int i = 0; i < pillarAmount; i++){
    		genes[i*2] = rand.getRand(0, Pillar.maxPosition);	
    		genes[i*2+1] = rand.getRand(0, Pillar.maxRadius);
    		stepSizes[i*2] = Pillar.maxPosition/Math.sqrt(10);//div 10
    		stepSizes[i*2+1] = Pillar.maxRadius/Math.sqrt(10);//div 10
    	}
		GenePillarHallway gene = new GenePillarHallway();
    	gene.setGenes(genes);
    	gene.setStepSizes(stepSizes);
    	gene.setLearningRate(learningRate);
    	gene.setGlobalLearningRate(globalLearningRate);
		return gene;
	}
	@Override
	public String printGenes() {
		String returnString = "";
		for(int i = 0; i < genes.length; i+=2){
			returnString += ("Gene: Y:"+genes[i]+" Radius:"+genes[i+1])+'\n';
		}
		return returnString;
	}
	@Override
	public String toString(){
		String ret = "";
		for(int i = 0; i < genes.length; i+=2){
			ret += " "+(i/2+1)+" Y:"+genes[i]+" Radius:"+genes[i+1];
		}
		return ret;
	}
}
